﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Newtonsoft.Json.Linq;

public class FM_Score:MonoBehaviour// View
{
    public  FM_ScoreBase ScoreBase;

    private Color Color = Color.black;
    private Color StaveLineColor = new Color(1,1,1);
    private bool ShowBrace;
    private float NoteSpacing;
    int width, height;
    public Paint Font;
    private float _DistanceBetweenStaveLines;
    private float DistanceBetweenStave_cnt;
    private float DistanceBetweenRows_cnt;
    private float PaddingV_cnt, PaddingS_p, PaddingE_p;
    private float PaddingS, PaddingE;
    private float PaddingTop = 10;
    private bool StartBar;
    private bool EndBar;
    private int Lines;
    //private readonly Context context;
    private FM_Align Align;

    private bool CenterVertical = true;
    private bool MultiRow = false;
    private bool AllowZoomPan = false;
    private bool AllowZoomControls = false;
    private bool tmpZoomControls = false;

    //private readonly ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.0f;
    private float mPosX = 0;
    private float mPosY = 0;
    private float mLastTouchX;
    private float mLastTouchY;
    private int mActivePointerId = INVALID_POINTER_ID;
    public const int INVALID_POINTER_ID = -1;

    float pivotPointX = 0f;
    float pivotPointY = 0f;
    int sectionPerLine = 4; // 每行的小节数

    private bool TrimLastRow;
    public int progressBar;
    private FM_BoundingBoxType DrawBoundingBox;
    private float startY;
    private float startX;
    Color recordLineColor;
    public bool showRecordTrack;
    public FM_ScoreBase dynamicScore;
    // protected final List<FM_BaseNote> dynamicNotes = new ArrayList<>();
    // private CountDownLatch finishedDraw = null;
    public Paint rightPaint;
    public Paint wrongPaint;
    bool ShowRecordTrack;

    void Start()
    {

    }
    public void Init(int _width,int _height)
    {
        ScoreBase = new FM_ScoreBase(this);
        dynamicScore = new FM_ScoreBase(this);
        this.width = _width;
        this.height = _height;
        //mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        Color = new Color(26 / 255f, 28 / 255f, 33 / 255f);
        SetStaveLineColor(new Color(50 / 255f, 50 / 255f, 50 / 255f));
        recordLineColor = new Color(50 / 255f, 50 / 255f, 255 / 255f);
        Font = new Paint();
        //Font.AntiAlias = true;
        this.Font.SetTextSize(28);
        var bravura = Resources.Load<Font>("bravura");
        Font.SetTypeface(bravura);
        Font.Color = Color;
        Lines = 1;
        TrimLastRow = false;
        progressBar = -1;
        SetNoteSpacing(5);
        SetShowBrace(false);
        SetDistanceBetweenStaveLines(10);
        SetDistanceBetweenStaves(5);
        SetDistanceBetweenRows(10);
        SetPaddingVertical(5);
        SetPaddingTop(10);
        SetPaddingS(5);
        SetPaddingE(5);
        SetStartBar(true);
        SetEndBar(true);
        SetMultiRow(false);
        SetNotesAlign(FM_Align.ALIGN_CENTER_MEASURES);
        ShowBoundingBoxes(FM_BoundingBoxType.None);

        rightPaint = new Paint();
        rightPaint.Color = Color.green;
        rightPaint.TextSize = 16;
        //rightPaint.AntiAlias = true;

        wrongPaint = new Paint();
        wrongPaint.Color = Color.red;
        wrongPaint.TextSize = 16;
        //wrongPaint.AntiAlias = true;
    }
    public void ProgressReset()
    {
        progressBar = -1;
        //Invalidate();
    }

    public void ProgressSetStart(int measure)
    {
        if (measure == 1)
        {
            progressBar = -1;
        }
        else
        {
            for (int i = 0; i < ScoreBase.StaveNotes.Count; i++)
            {
                if (ScoreBase.StaveNotes[i].GetNoteType() == FM_NoteType.BAR) measure--;
                if (measure == 1)
                {
                    progressBar = i;
                    break;
                }
            }
        }
        //Invalidate();
    }

    public void ProgressAdvance(bool checkNoteWrong)
    {
        if (checkNoteWrong)
            CheckNoteRight(progressBar);

        progressBar += 1;
        if (ScoreBase.StaveNotes[progressBar] is FM_BarNote) progressBar += 1;
        if (ScoreBase.StaveNotes[progressBar] is FM_Clef) progressBar += 1;
        if (ScoreBase.StaveNotes[progressBar] is FM_KeySignature) progressBar += 1;
        //Invalidate();
    }

    Queue<int> toCheckProgressBar = new Queue<int>();
    Dictionary<int, RightNWrong> notesRight = new Dictionary<int, RightNWrong>();

    void CheckNoteRight(int bar)
    {
        toCheckProgressBar.Enqueue(bar);

        int peek = toCheckProgressBar.Peek();
        if (peek <= bar - 2)
            peek = toCheckProgressBar.Dequeue();

        DoCheckNoteRight(peek);
    }

    void DoCheckNoteRight(int bar)
    {
        if (notesRight.ContainsKey(bar))
        {
            RightNWrong r = notesRight[bar];
            if (r != null && r.right)
            {
                return;
            }
        }
        AddSign(bar, false, -1);
    }

    public void OnPlayEnd()
    {
        while (toCheckProgressBar.Count > 0)
        {
            DoCheckNoteRight(toCheckProgressBar.Dequeue());
        }
        ProgressReset();
    }

    public void SetShowRecordTrack(bool show)
    {
        ShowRecordTrack = show;
    }

    public void SetTrimLastRow(bool trimLastRow)
    {
        TrimLastRow = trimLastRow;
        //Invalidate();
    }

    public bool IsTrimLastRow()
    {
        return TrimLastRow;
    }

    public void ShowBoundingBoxes(FM_BoundingBoxType show)
    {
        DrawBoundingBox = show;
        //Invalidate();
    }

    public FM_BoundingBoxType GetShowBoundingBoxes()
    {
        return DrawBoundingBox;
    }

    public bool GetCenterVertical()
    {
        return CenterVertical;
    }

    public void SetCenterVertical(bool center)
    {
        CenterVertical = center;
        CapTranslateVars();
        //Invalidate();
    }

    /**
 * @return Return the numerator
 */
    public FM_TimeSignatureValue GetTimeSignature_n()
    {
        return ScoreBase.GetTimeSignature_n();
    }

    /**
     * @return Return the denominator
     */
    public FM_TimeSignatureValue GetTimeSignature_d()
    {
        return ScoreBase.GetTimeSignature_d();
    }

    public void SetTimeSignature(FM_TimeSignatureValue numerator, FM_TimeSignatureValue denominator)
    {
        ScoreBase.SetTimeSignature(numerator, denominator);
        // Invalidate();
        //RequestLayout();
    }

    private void RequestLayout()
    {
        Init(this.width,this.height);
    }

    public FM_StaveCount GetStaveCount()
    {
        return ScoreBase.GetStaveCount();
    }

    public int GetVoiceCount()
    {
        return ScoreBase.GetVoiceCount();
    }

    public void SetVoiceCount(int voiceCount)
    {
        ScoreBase.SetVoiceCount(voiceCount);
    }

    public bool IsShowBrace()
    {
        return ShowBrace;
    }

    public void SetShowBrace(bool showBrace)
    {
        ShowBrace = showBrace;
    }

    private float GetDrawHeight()
    {
        float MaxHeight = Lines * (4 * GetDistanceBetweenStaveLines()) + (Lines - 1) * GetDistanceBetweenRows() + 2 * PaddingV_cnt * GetDistanceBetweenStaveLines();

        if (ScoreBase.StaveCount == FM_StaveCount._2)
            MaxHeight = MaxHeight + Lines * (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines());

        // if (MaxHeight < height) return height;
        if (showRecordTrack)
        {
            MaxHeight *= 2;
        }
        return MaxHeight + PaddingTop * 2;
    }

    bool logged;

    // List<RightNWrong> rightNWrong = new ArrayList<>();
    public void DoDraw(ScoreCanvas canvas)
    {
        canvas.Clear();
        //Matrix matrix = new Matrix();
        //matrix.PostScale(mScaleFactor, mScaleFactor, 0, 0);
        float adjustY = 0;
        if (CenterVertical) adjustY = height / 2f - GetDrawHeight() / 2f;
        //matrix.PostTranslate(mPosX, mPosY + adjustY);
        //canvas.SetMatrix(matrix);

        float staveLineHalfWidth = FM_Const.dpTOpx(0.25f);
        float ys1 = PaddingTop + GetPaddingVertical();
        float ys2 = PaddingTop;
        float barYs = 0;
        float barYe = 0;

        int allLines = Lines;
        if (ShowRecordTrack)
            allLines = Lines * 2;

        // Draw stave lines
        for (int index = 0; index < allLines; index++)
        {
            int l = index;
            if (ShowRecordTrack)
                l = index / 2;

            ys2 = ys1;
            barYs = ys1;
            barYe = ys1 + 4 * GetDistanceBetweenStaveLines();

            // Draw stave lines
            Font.SetColor(StaveLineColor);
            if (ShowRecordTrack && index % 2 == 1)
                Font.SetColor(recordLineColor);

            if (!TrimLastRow || (l < Lines - 1))
            {
                for (int i = 0; i < 5; i++)
                {
                    canvas.DrawRect(PaddingS, ys1 + i * GetDistanceBetweenStaveLines() - staveLineHalfWidth, width - PaddingE, ys1 + i * GetDistanceBetweenStaveLines() + staveLineHalfWidth,StaveLineColor);
                }
            }
            else
            {
                for (int i = 0; i < 5; i++)
                {
                    canvas.DrawRect(PaddingS, ys1 + i * GetDistanceBetweenStaveLines() - staveLineHalfWidth, GetLineWidth(l + 1), ys1 + i * GetDistanceBetweenStaveLines() + staveLineHalfWidth, StaveLineColor);
                }
            }

            Font.SetColor(Color);

            // Draw clef
            var clef = ScoreBase.FirstStaveClef;
            for (int n = 0; n < ScoreBase.StaveNotes.Count; n++)
            {
                if (ScoreBase.StaveNotes[n].line - 1 == l && ScoreBase.StaveNotes[n].stave == 0)
                {
                    if (ScoreBase.StaveNotes[n] is FM_Clef)
                    {
                        clef = ((FM_Clef)ScoreBase.StaveNotes[n]).clef;
                    }
                    break;
                }
            }

            if (clef == FM_ClefValue.TREBLE)
                DrawTrebleClef(canvas, ys1);
            else
                DrawBassClef(canvas, ys1);

            // Draw keySignature
            ScoreBase.FirstStaveKey.SetDrawParameters(PaddingS + GetClefWidth(), ys1, ys1);
            ScoreBase.FirstStaveKey.DrawNote(canvas, clef);

            // Draw timeSignature
            if (l == 0)
                DrawTimeSignature(canvas, ys1);

            if (ScoreBase.StaveCount == FM_StaveCount._2)
            {
                ys2 = ys1 + (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines());
                barYe = ys2 + 4 * GetDistanceBetweenStaveLines();

                Font.SetColor(StaveLineColor);
                if (ShowRecordTrack && index % 2 == 1)
                    Font.SetColor(recordLineColor);

                if (!TrimLastRow || (l < Lines - 1))
                {
                    for (int i = 0; i < 5; i++)
                    {
                        canvas.DrawRect(PaddingS, ys2 + i * GetDistanceBetweenStaveLines() - staveLineHalfWidth, width - PaddingE, ys2 + i * GetDistanceBetweenStaveLines() + staveLineHalfWidth, StaveLineColor);
                    }
                }
                else
                {
                    for (int i = 0; i < 5; i++)
                    {
                        canvas.DrawRect(PaddingS, ys2 + i * GetDistanceBetweenStaveLines() - staveLineHalfWidth, GetLineWidth(l + 1), ys2 + i * GetDistanceBetweenStaveLines() + staveLineHalfWidth, StaveLineColor);
                    }
                }

                Font.SetColor(Color);

                // Draw clef
                clef = ScoreBase.SecondStaveClef;
                for (int n = 0; n < ScoreBase.StaveNotes.Count; n++)
                {
                    if (ScoreBase.StaveNotes[n].line - 1 == l && ScoreBase.StaveNotes[n].stave == 1)
                    {
                        if (ScoreBase.StaveNotes[n] is FM_Clef)
                        {
                            clef = ((FM_Clef)ScoreBase.StaveNotes[n]).clef;
                        }
                        break;
                    }
                }

                if (clef == FM_ClefValue.TREBLE)
                    DrawTrebleClef(canvas, ys2);
                else
                    DrawBassClef(canvas, ys2);

                ScoreBase.SecondStaveKey.SetDrawParameters(PaddingS + GetClefWidth(), ys2, ys2);
                ScoreBase.SecondStaveKey.DrawNote(canvas, clef);

                if (l == 0)
                    DrawTimeSignature(canvas, ys2);

                // Draw Bracket
                if (ShowBrace)
                {
                    Paint f = new Paint();
                    //f.AntiAlias = true;
                    f.SetTypeface(Font.Typeface);
                    Rect bounds = new Rect();
                    f.GetTextBounds(FM_Const.Bracket, 0, 1, bounds);
                    int height = (int)bounds.height;//.Height();
                    f.TextSize = f.TextSize * (barYe - barYs) / height;
                    f.Color = Font.Color;
                    canvas.DrawText(FM_Const.Bracket, PaddingS - f.MeasureText(FM_Const.Bracket) - FM_Const.dpTOpx(2), ys2 + 4 * GetDistanceBetweenStaveLines(), f);
                }
                // End Draw Bracket
            }

            Font.SetColor(Color);

            if (StartBar)
                canvas.DrawRect(PaddingS - FM_Const.dpTOpx(1), barYs - staveLineHalfWidth, PaddingS, barYe + staveLineHalfWidth, StaveLineColor);

            if (EndBar)
            {
                if (!TrimLastRow || (l < Lines - 1))
                {
                    canvas.DrawRect(width - PaddingE, barYs - staveLineHalfWidth, width - PaddingE + FM_Const.dpTOpx(1), barYe + staveLineHalfWidth, StaveLineColor);
                }
                else
                {
                    canvas.DrawRect(GetLineWidth(l + 1), barYs - staveLineHalfWidth, GetLineWidth(l + 1) + FM_Const.dpTOpx(1), barYe + staveLineHalfWidth, StaveLineColor);
                }
            }

            ys1 = ys2 + (GetDistanceBetweenRows() + 4 * GetDistanceBetweenStaveLines());
        }
        //Debug.LogError($"StaveNotes:{ScoreBase.StaveNotes.Count}  Beams:{ScoreBase.Beams.Count}  Ties:{ScoreBase.Ties.Count}  Tuplets:{ScoreBase.Tuplets.Count}");
        // Draw notes

        for (int i = 0; i < ScoreBase.StaveNotes.Count; i++)
            ScoreBase.StaveNotes[i].DrawNote(canvas);

        for (int i = 0; i < dynamicScore.StaveNotes.Count; i++)
            dynamicScore.StaveNotes[i].DrawNote(canvas);

        for (int j = 0; j < ScoreBase.Ties.Count; j++)
            ScoreBase.Ties[j].Draw(canvas);

        for (int j = 0; j < ScoreBase.Beams.Count; j++)
            ScoreBase.Beams[j].Draw(canvas);

        for (int j = 0; j < ScoreBase.Tuplets.Count; j++)
            ScoreBase.Tuplets[j].Draw(canvas);


        if (EndBar)
        {
            Font.Color = Color;
            if (TrimLastRow)
            {
                canvas.DrawRect(GetLineWidth(Lines) - FM_Const.dpTOpx(GetDistanceBetweenStaveLines() / 7), barYs - staveLineHalfWidth, GetLineWidth(Lines), barYe + staveLineHalfWidth, StaveLineColor);
                canvas.DrawRect(GetLineWidth(Lines) - FM_Const.dpTOpx(GetDistanceBetweenStaveLines() * 2 / 7), barYs - staveLineHalfWidth, GetLineWidth(Lines) - FM_Const.dpTOpx( GetDistanceBetweenStaveLines() * 17 / 70), barYe + staveLineHalfWidth, StaveLineColor);
            }
            else
            {
                canvas.DrawRect(width - PaddingE - FM_Const.dpTOpx(GetDistanceBetweenStaveLines() / 7), barYs - staveLineHalfWidth, width - PaddingE, barYe + staveLineHalfWidth, StaveLineColor);
                canvas.DrawRect(width - PaddingE - FM_Const.dpTOpx(GetDistanceBetweenStaveLines() * 2 / 7), barYs - staveLineHalfWidth, width - PaddingE - FM_Const.dpTOpx( GetDistanceBetweenStaveLines() * 17 / 70), barYe + staveLineHalfWidth, StaveLineColor);
            }
        }

        //canvas.Restore();

        //if (AllowZoomControls)
        //{
        //    Paint zoomBtnFnt = new Paint();
        //    zoomBtnFnt.StrokeWidth = 2;
        //    zoomBtnFnt.SetStyle(Paint.Style.Stroke);
        //    zoomBtnFnt.Color = Android.Graphics.Color.Argb(150, 50, 50, 50);
        //    zoomBtnFnt.AntiAlias = true;

        //    canvas.DrawRoundRect(width - 110, height - 110, width - 10, height - 10, 10, 10, zoomBtnFnt);
        //    canvas.DrawRoundRect(width - 220, height - 110, width - 120, height - 10, 10, 10, zoomBtnFnt);

        //    zoomBtnFnt.Color = Android.Graphics.Color.Argb(200, 0, 0, 0);
        //    zoomBtnFnt.StrokeWidth = 5;

        //    canvas.DrawLine(width - 90, height - 60, width - 30, height - 60, zoomBtnFnt);
        //    canvas.DrawLine(width - 60, height - 90, width - 60, height - 30, zoomBtnFnt);
        //    canvas.DrawLine(width - 190, height - 60, width - 150, height - 60, zoomBtnFnt);
        //}
    }
    GameObject progressBarUI;
    public void UpdateCursorShow(ScoreCanvas canvas)
    {
        // Draw cursor
        if (progressBar > -1)
        {
            int line = ScoreBase.StaveNotes[progressBar].line;
            float x = ScoreBase.StaveNotes[progressBar].StartX + ScoreBase.StaveNotes[progressBar].Width() + FM_Const.dpTOpx(3);
            //Paint f = new Paint();
            //f.AntiAlias = true;
            //f.Color = Color.red;

            float ys = PaddingTop + GetPaddingVertical();
            float ye = PaddingTop + GetPaddingVertical() + 4 * GetDistanceBetweenStaveLines();
            float mul = (GetDistanceBetweenRows() + 4 * GetDistanceBetweenStaveLines());

            if (ScoreBase.StaveCount == FM_StaveCount._2)
            {
                ye = ye + GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines();
                mul = mul + GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines();
            }

            if (ShowRecordTrack)
            {
                line = line * 2 - 1;
            }

            ys = ys + (line - 1) * mul;
            ye = ye + (line - 1) * mul;

            if (progressBarUI)
                DestroyImmediate(progressBarUI);

            progressBarUI = canvas.DrawRect(x, ys, x + FM_Const.dpTOpx(1), ye, Color.red, "ProgressBar").gameObject;

            mPosY = -(line - 1) * (GetDistanceBetweenStaves());
            if (ScoreBase.StaveCount == FM_StaveCount._2)
                mPosY -= (line - 1) * (GetDistanceBetweenStaves());

            if (mPosY < (height - mScaleFactor * GetDrawHeight()))
            {
                mPosY = (height - mScaleFactor * GetDrawHeight());
            }

            if (mPosY > 0)
                mPosY = 0;
        }
        else{
            if (progressBarUI)
                DestroyImmediate(progressBarUI);
            progressBarUI = null;

        }
    }

    List<GameObject> signs = new List<GameObject>();
    public void UpdateSignShow(ScoreCanvas canvas)
    {
        for (int i = 0; i < signs.Count; i++)
        {
            Destroy(signs[i].gameObject);
        }
        signs.Clear();
        // Draw right and wrong signs
        foreach (var entry in notesRight)
        {
            var text = entry.Value.Draw(canvas, rightPaint, wrongPaint);
            signs.Add(text.gameObject);
        }
    }


    void AddSign(RightNWrong sign)
    {
        if (notesRight.ContainsKey(sign.progressBar))
        {
            if (!notesRight[sign.progressBar].right)
                notesRight[sign.progressBar] = sign;
        }
        else
            notesRight.Add(sign.progressBar, sign);
        // this.rightNWrong.add(sign);
    }

    public void AddSign(int progressBar, bool right, int stave)
    {
        if (progressBar >= 0 && progressBar < ScoreBase.StaveNotes.Count)
        {
            FM_BaseNote curNote = ScoreBase.StaveNotes[progressBar];
            // float x = curNote.getStartX();
            if (curNote is FM_Chord)
            {
                FM_Chord chord = (FM_Chord)curNote;
                if (chord.Notes.Count > 0)
                    foreach (FM_BaseNote fmNote in chord.Notes)
                    {
                        if (stave >= 0 && fmNote.stave != stave)
                            continue;

                        if (fmNote is FM_Note)
                        {
                            FM_Note note = (FM_Note)fmNote;
                            if (note.isTieEnd)
                                continue;
                        }
                        else
                        {
                            continue;
                        }

                        float x = fmNote.GetStartX();

                        int line = curNote.line;
                        if (showRecordTrack)
                            line = 2 * line - 1;

                        int cnt = 4 + sectionPerLine;
                        float y = PaddingTop + GetPaddingVertical() + (line - 1) * (GetDistanceBetweenStaves() + cnt * GetDistanceBetweenStaveLines()) + 6 * GetDistanceBetweenStaveLines();

                        if (ScoreBase.StaveCount == FM_StaveCount._2)
                        {
                            y += (line - 1) * (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines()) + fmNote.stave * (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines());
                        }

                        RightNWrong sign = new RightNWrong();
                        sign.x = x;
                        sign.y = y;
                        sign.right = right;
                        sign.progressBar = progressBar;
                        AddSign(sign);
                        //Debug.Log("AddSign chord.Notes.size():" + chord.Notes.Count + " progressBar:" + progressBar);
                    }
            }
        }
    }

    public Color GetColor()
    {
        return Color;
    }

    public bool GetStartBar()
    {
        return StartBar;
    }

    public void SetStartBar(bool b)
    {
        StartBar = b;
        // Invalidate();
        // RequestLayout();
    }

    public int GetLines()
    {
        return Lines;
    }

    public bool GetEndBar()
    {
        return EndBar;
    }

    public void SetEndBar(bool b)
    {
        EndBar = b;
        // Invalidate();
        // RequestLayout();
    }

    public float GetPaddingS()
    {
        return PaddingS;
    }

    public void SetPaddingS(float percent)
    {
        PaddingS_p = percent;
        if (width < 100) width = 100;
        PaddingS = percent * width / 100f;
        //Invalidate();
    }

    public float GetPaddingVertical()
    {
        return PaddingV_cnt * GetDistanceBetweenStaveLines();
    }

    public void SetPaddingVertical(float count)
    {
        PaddingV_cnt = count;
        //Invalidate();
    }

    public void SetPaddingTop(int padding)
    {
        Debug.LogError($"SetPaddingTop:{padding}");
        this.PaddingTop = padding;
    }

    public float GetPaddingE()
    {
        return PaddingE;
    }

    public void SetPaddingE(float percent)
    {
        PaddingE_p = percent;
        if (width < 100) width = 100;
        PaddingE = percent * width / 100f;
        // Invalidate();
        // RequestLayout();
    }

    public float GetDistanceBetweenStaveLines()
    {
        return _DistanceBetweenStaveLines;
    }

    public void SetDistanceBetweenStaveLines(float d)
    {
        if (d < 4) d = 4;
        if (d > 20) d = 20;
        _DistanceBetweenStaveLines = FM_Const.dpTOpx(d);
        Font.SetTextSize(FM_Const.dpTOpx(5f * d));
        this.ComputeLines();

        //Invalidate();
        // RequestLayout();
    }

    public float GetDistanceBetweenRows()
    {
        return DistanceBetweenRows_cnt * GetDistanceBetweenStaveLines();
    }

    public void SetDistanceBetweenRows(float d)
    {
        DistanceBetweenRows_cnt = d;
        // Invalidate();
        // RequestLayout();
    }

    public float GetDistanceBetweenStaves()
    {
        return DistanceBetweenStave_cnt * GetDistanceBetweenStaveLines();
    }

    public void SetDistanceBetweenStaves(float d)
    {
        DistanceBetweenStave_cnt = d;
        // Invalidate();
        // RequestLayout();
    }

    private bool IsAClick(float startX, float endX, float startY, float endY)
    {
        float differenceX = Math.Abs(startX - endX);
        float differenceY = Math.Abs(startY - endY);
        int CLICK_ACTION_THRESHOLD = 10;
        return !(differenceX > CLICK_ACTION_THRESHOLD || differenceY > CLICK_ACTION_THRESHOLD);
    }

    private void CapTranslateVars()
    {
        if (mPosX > 0) mPosX = 0;
        if (mPosX < width * (1 - mScaleFactor)) mPosX = width * (1 - mScaleFactor);

        float adjustY = 0;
        if (CenterVertical)
        {
            adjustY = height / 2f - GetDrawHeight() / 2f;
            if (adjustY >= 0)
            {
                mPosY = -(mScaleFactor - 1) * GetDrawHeight() / 2f;
                return;
            }
        }
        mPosY += adjustY;
        if (mPosY < (height - mScaleFactor * GetDrawHeight()))
        {
            mPosY = (height - mScaleFactor * GetDrawHeight());
        }
        if (mPosY > 0) mPosY = 0;
        mPosY -= adjustY;
    }

    //public override bool OnTouchEvent(MotionEvent e)
    //{
    //    if (!AllowZoomPan) return base.OnTouchEvent(e);
    //    mScaleDetector.OnTouchEvent(e);
    //    var action = e.Action;
    //    switch (action & MotionEventActions.Mask)
    //    {
    //        case MotionEventActions.Down:
    //            {
    //                var x = e.GetX();
    //                var y = e.GetY();
    //                startX = x;
    //                startY = y;
    //                mLastTouchX = x;
    //                mLastTouchY = y;
    //                mActivePointerId = e.GetPointerId(0);
    //                break;
    //            }

    //        case MotionEventActions.Move:
    //            {
    //                var pointerIndex = e.FindPointerIndex(mActivePointerId);
    //                float x = e.GetX(pointerIndex);
    //                float y = e.GetY(pointerIndex);

    //                // Only move if the ScaleGestureDetector isn't processing a gesture.
    //                if (!mScaleDetector.IsInProgress)
    //                {
    //                    var dx = x - mLastTouchX;
    //                    var dy = y - mLastTouchY;
    //                    mPosX += dx;
    //                    mPosY += dy;
    //                    //Invalidate();
    //                }

    //                mLastTouchX = x;
    //                mLastTouchY = y;
    //                CapTranslateVars();
    //                break;
    //            }

    //        case MotionEventActions.Up:
    //            {
    //                mActivePointerId = INVALID_POINTER_ID;
    //                if (IsAClick(startX, e.GetX(), startY, e.GetY()))
    //                {
    //                    bool save = false;
    //                    if (AllowZoomControls)
    //                    {
    //                        if (e.GetX() > width - 110 && e.GetX() < width - 10 && e.GetY() > height - 110 && e.GetY() < height - 10)
    //                        {
    //                            SetDistanceBetweenStaveLines(FM_Const.pxTOdp(_DistanceBetweenStaveLines + 1f));
    //                            save = true;
    //                        }
    //                        if (e.GetX() > width - 220 && e.GetX() < width - 120 && e.GetY() > height - 110 && e.GetY() < height - 10)
    //                        {
    //                            SetDistanceBetweenStaveLines(FM_Const.pxTOdp(_DistanceBetweenStaveLines - 1f));
    //                            save = true;
    //                        }
    //                        if (save)
    //                        {
    //                            var settings = PreferenceManager.GetDefaultSharedPreferences();
    //                            var editor = settings.Edit();
    //                            editor.PutFloat("zoom_level", FM_Const.pxTOdp(_DistanceBetweenStaveLines));
    //                            editor.Apply();
    //                        }
    //                    }
    //                    //if (!save) base.PerformClick();
    //                    return base.OnTouchEvent(e);
    //                }
    //                break;
    //            }

    //        case MotionEventActions.Cancel:
    //            {
    //                mActivePointerId = INVALID_POINTER_ID;
    //                break;
    //            }

    //        case MotionEventActions.PointerUp:
    //            {
    //                var pointerIndex = (int)(e.Action & MotionEventActions.PointerIndexMask) >> (int)MotionEventActions.PointerIndexShift;
    //                var pointerId = e.GetPointerId(pointerIndex);
    //                if (pointerId == mActivePointerId)
    //                {
    //                    // This was our active pointer going up. Choose a new
    //                    // active pointer and adjust accordingly.
    //                    var newPointerIndex = pointerIndex == 0 ? 1 : 0;
    //                    mLastTouchX = e.GetX(newPointerIndex);
    //                    mLastTouchY = e.GetY(newPointerIndex);
    //                    mActivePointerId = e.GetPointerId(newPointerIndex);
    //                }
    //                break;
    //            }
    //    }

    //    return true;
    //}

    public FM_ClefValue GetFirstStaveClef()
    {
        return ScoreBase.GetFirstStaveClef();
    }

    public void SetFirstStaveClef(FM_ClefValue firstStaveClef)
    {
        ScoreBase.SetFirstStaveClef(firstStaveClef);
        ScoreBase.SetKeySignature(ScoreBase.KeySignature);
    }

    public FM_ClefValue GetSecondStaveClef()
    {
        return ScoreBase.GetSecondStaveClef();
    }

    public void SetSecondStaveClef(FM_ClefValue secondStaveClef)
    {
        ScoreBase.SetSecondStaveClef(secondStaveClef);
        ScoreBase.SetKeySignature(ScoreBase.KeySignature);
    }

    /**
     * Set the default color for the stave elements (notes, pauses, clefs, etc).
     * If you don't set it yourself, the default is android.graphics.Color.argb(255, 26, 28, 33).
     * @param color the drawing color
     */
    public void SetColor(Color color)
    {
        Color = color;
        Font.SetColor(color);
        ScoreBase.FirstStaveKey.SetColor(color);
        ScoreBase.SecondStaveKey.SetColor(color);
        // Invalidate();
        // RequestLayout();
    }

    public FM_KeySignatureValue GetKeySignature()
    {
        return ScoreBase.GetKeySignature();
    }

    public void SetKeySignature(FM_KeySignatureValue keySignature)
    {
        ScoreBase.SetKeySignature(keySignature);
    }

    protected float GetClefWidth()
    {
        FM_Const.AdjustFont(this, FM_Const._4, 2);
        float w = Font.MeasureText(FM_Const.TrebleClef) + 2 * FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING);
        float w1 = Font.MeasureText(FM_Const.BassClef) + 2 * FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING);
        return Math.Max(w, w1);
    }

    private void DrawTrebleClef(ScoreCanvas canvas, float y)
    {
        FM_Const.AdjustFont(this, FM_Const._4, 2);
        Font.SetColor(Color);
        canvas.DrawText(FM_Const.TrebleClef, PaddingS + FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING), y + 3 * GetDistanceBetweenStaveLines(), Font);
    }

    private void DrawBassClef(ScoreCanvas canvas, float y)
    {
        FM_Const.AdjustFont(this, FM_Const._4, 2);
        Font.SetColor(Color);
        canvas.DrawText(FM_Const.BassClef, PaddingS + FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING), y + 1 * GetDistanceBetweenStaveLines(), Font);
    }

    protected float GetTimeSignatureWidth()
    {
        if (ScoreBase.TimeSignature_n == FM_TimeSignatureValue.None) return 0;
        float w = FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING);
        FM_Const.AdjustFont(this, FM_Const._4, 2);
        return w + Font.MeasureText(FM_Const._4);
    }

    private void DrawTimeSignature(ScoreCanvas canvas, float y)
    {
        Font.SetColor(Color);
        float pad = PaddingS + GetClefWidth() + ScoreBase.FirstStaveKey.Width();
        FM_Const.AdjustFont(this, FM_Const._4, 2);

        switch (ScoreBase.TimeSignature_n)
        {
            case FM_TimeSignatureValue._2:
                canvas.DrawText(FM_Const._2, pad, y + 1 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._3:
                canvas.DrawText(FM_Const._3, pad, y + 1 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._4:
                canvas.DrawText(FM_Const._4, pad, y + 1 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._5:
                canvas.DrawText(FM_Const._5, pad, y + 1 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._6:
                canvas.DrawText(FM_Const._6, pad, y + 1 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._7:
                canvas.DrawText(FM_Const._7, pad, y + 1 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._8:
                canvas.DrawText(FM_Const._8, pad, y + 1 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._9:
                canvas.DrawText(FM_Const._9, pad, y + 1 * GetDistanceBetweenStaveLines(), Font);
                break;
        }

        switch (ScoreBase.TimeSignature_d)
        {
            case FM_TimeSignatureValue._2:
                canvas.DrawText(FM_Const._2, pad, y + 3 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._3:
                canvas.DrawText(FM_Const._3, pad, y + 3 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._4:
                canvas.DrawText(FM_Const._4, pad, y + 3 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._5:
                canvas.DrawText(FM_Const._5, pad, y + 3 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._6:
                canvas.DrawText(FM_Const._6, pad, y + 3 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._7:
                canvas.DrawText(FM_Const._7, pad, y + 3 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._8:
                canvas.DrawText(FM_Const._8, pad, y + 3 * GetDistanceBetweenStaveLines(), Font);
                break;
            case FM_TimeSignatureValue._9:
                canvas.DrawText(FM_Const._9, pad, y + 3 * GetDistanceBetweenStaveLines(), Font);
                break;
        }

    }

    public void ClearStaveNotes()
    {
        ScoreBase.ClearStaveNotes();
        mPosX = 0;
        mPosY = 0;
        Lines = 1;
        progressBar = -1;
        //Invalidate();
    }

    public void AddStaveNote(FM_BaseNote n)
    {
        AddStaveNote(n, 0);
    }

    public void AddStaveNote(FM_BaseNote n, int stave)
    {
        ScoreBase.AddStaveNote(n, stave);
        ComputeLines();
    }

    public void AddChord(List<FM_BaseNote> n, List<int> stave)
    {
        ScoreBase.AddChord(n, stave);
        ComputeLines();
    }

    private float GetStartX(int line)
    {
        if (line == 1)
            return PaddingS + GetClefWidth() + ScoreBase.FirstStaveKey.Width() + GetTimeSignatureWidth() + FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING);
        else
            return PaddingS + GetClefWidth() + ScoreBase.FirstStaveKey.Width() + FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING);
    }

    private float GetLineWidth(int line)
    {
        float X = 0;
        float w = 0;
        for (int j = 0; j < ScoreBase.StaveNotes.Count; j++)
            if (ScoreBase.StaveNotes[j].line == line)
            {
                if (X < ScoreBase.StaveNotes[j].StartX)
                {
                    X = ScoreBase.StaveNotes[j].StartX;
                    w = ScoreBase.StaveNotes[j].Width();
                }
            }
        float ret = X + w + 4 * FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING);
        if (ret > width - PaddingE) ret = width - PaddingE;
        return ret;
    }

    private void ComputeLines()
    {
        if (ScoreBase.StaveNotes.Count == 0) return;
        int l = 1;
        float endX = width - PaddingE - 2 * FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING) - FM_Const.dpTOpx(10);
        float ys1 = GetPaddingVertical();
        float ys2 = GetPaddingVertical();
        if (ScoreBase.StaveCount == FM_StaveCount._2)
            ys2 = ys1 + (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines());

        // set everything to be visible
        foreach (var note in ScoreBase.StaveNotes)
        {
            note.visible = true;
            if (note is FM_BarNote barNote)
                barNote.lineEnd = false;
        }

        if (MultiRow && Align == FM_Align.ALIGN_LEFT_NOTES)
        {
            float X = GetStartX(l);
            int noteIndex = 0;
            FM_BaseNote last_note = null;
            bool already_a_clef = false;
            foreach (var note in ScoreBase.StaveNotes)
            {
                float w = note.Width() + NoteSpacing;
                if (noteIndex == 0 && note is FM_Clef clefNote)
                {
                    w = 0;
                    clefNote.visible = false;
                    already_a_clef = true;
                }
                if (already_a_clef && noteIndex != 0 && note is FM_Clef clefNote2)
                {
                    w = 0;
                    clefNote2.visible = false;
                    already_a_clef = true;
                }
                noteIndex++;
                if (X + w > endX)
                {
                    if (last_note is FM_BarNote)
                    {
                        ((FM_BarNote)last_note).lineEnd = true;
                        last_note.visible = false;
                    }
                    l++;
                    noteIndex = 0;
                    X = GetStartX(l);
                    ys1 = ys2 + (GetDistanceBetweenRows() + 4 * GetDistanceBetweenStaveLines());
                    ys2 = ys1;
                    already_a_clef = false;
                }
                if (ScoreBase.StaveCount == FM_StaveCount._2) ys2 = ys1 + (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines());
                if (note.stave == 0) note.SetDrawParameters(X, ys1, ys2);
                if (note.stave == 1) note.SetDrawParameters(X, ys2, ys2);
                note.line = l;
                X = X + w;
                last_note = note;
            }
            // If the last note is a bar, hide it
            if (ScoreBase.StaveNotes.LastOrDefault() is FM_BarNote lastBarNote)
            {
                lastBarNote.lineEnd = true;
                lastBarNote.visible = false;
            }
        }

        float scale = 1.15f;
        if (Align == FM_Align.ALIGN_LEFT_LAST_MEASURE) scale = 1f;
        if (Align == FM_Align.ALIGN_CENTER_NOTES) scale = 1.0f;
        if (MultiRow && (Align == FM_Align.ALIGN_CENTER_MEASURES || Align == FM_Align.ALIGN_CENTER_NOTES || Align == FM_Align.ALIGN_LEFT_MEASURES || Align == FM_Align.ALIGN_LEFT_LAST_MEASURE))
        {
            float X = GetStartX(l);
            int last_bar = 0;
            int bar_cnt = 0;
            int noteIndex = 0;
            var last_clef = GetFirstStaveClef();
            for (int i = 0; i < ScoreBase.StaveNotes.Count; i++)
            {
                noteIndex++;
                float widthIncrement = ScoreBase.StaveNotes[i].Width() + NoteSpacing;
                if (ScoreBase.StaveNotes[i] is FM_Clef clefNote)
                {
                    if (noteIndex == 1)
                    {
                        widthIncrement = 0;
                        clefNote.visible = false;
                    }
                    else if (last_clef == clefNote.clef)
                    {
                        widthIncrement = 0;
                        clefNote.visible = false;
                    }
                    last_clef = clefNote.clef;
                }
                if (ScoreBase.StaveNotes[i] is FM_BarNote)
                {
                    last_bar = i;
                    bar_cnt++;
                }
                if (bar_cnt > (sectionPerLine - 1) && X + widthIncrement > endX * scale)
                {
                    var lBarNote = ScoreBase.StaveNotes[last_bar];
                    lBarNote.line = l;
                    lBarNote.visible = false;
                    ((FM_BarNote)lBarNote).lineEnd = true;

                    l++;
                    noteIndex = 0;
                    X = GetStartX(l);
                    ys1 = ys2 + (GetDistanceBetweenRows() + 4 * GetDistanceBetweenStaveLines());
                    ys2 = ys1;
                    bar_cnt = 0; 
                    i = last_bar;
                    continue;
                }
                if (ScoreBase.StaveCount == FM_StaveCount._2)
                    ys2 = ys1 + (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines());
                if (ScoreBase.StaveNotes[i].stave == 0) ScoreBase.StaveNotes[i].SetDrawParameters(X, ys1, ys2);
                if (ScoreBase.StaveNotes[i].stave == 1) ScoreBase.StaveNotes[i].SetDrawParameters(X, ys2, ys2);
                ScoreBase.StaveNotes[i].line = l;
                X = X + widthIncrement;
            }
            // If the last note is a bar, hide it
            if (ScoreBase.StaveNotes.LastOrDefault() is FM_BarNote lastBarNote)
            {
                lastBarNote.lineEnd = true;
                lastBarNote.visible = false;
            }
        }

        if (!MultiRow)
        {
            // If the last note is a bar, hide it
            if (ScoreBase.StaveNotes.LastOrDefault() is FM_BarNote lastBarNote)
            {
                lastBarNote.lineEnd = true;
                lastBarNote.visible = false;
            }
        }
        Lines = l;

        Debug.Log($"line:" + l+ "  Align:"+ Align);

        if (Align == FM_Align.ALIGN_CENTER_NOTES || Align == FM_Align.ALIGN_CENTER_NOTES_ALL)
        {
            for (int k = 1; k <= Lines; k++)
            {
                int line = k;
                float X = GetStartX(line);
                int cnt = 0;
                float diff;
                foreach (var note in ScoreBase.StaveNotes)
                {
                    if (note.line == line)
                    {
                        float w = note.Width();
                        if (!note.visible) w = 0;
                        X = X + w;
                        cnt++;
                    }
                }
                float s = (endX - X) / (cnt + 1);
                diff = (endX - X) / (cnt - 1);
                X = GetStartX(line);
                if (Align == FM_Align.ALIGN_CENTER_NOTES_ALL)
                {
                    foreach (var note in ScoreBase.StaveNotes)
                    {
                        if (note.line == line)
                        {
                            float w = note.Width();
                            if (!note.visible) w = 0;
                            note.SetDrawParameters(X, note.StartY1, note.StartY2);
                            X = X + w + diff;
                        }
                    }
                }
                else
                {
                    foreach (var note in ScoreBase.StaveNotes)
                    {
                        if (note.line == line)
                        {
                            float w = note.Width();
                            if (!note.visible) w = 0;
                            note.SetDrawParameters(X + s, note.StartY1, note.StartY2);
                            X = X + w + s;
                        }
                    }
                }
            }
        }

        if (Align == FM_Align.ALIGN_CENTER_MEASURES || Align == FM_Align.ALIGN_LEFT_MEASURES || Align == FM_Align.ALIGN_LEFT_LAST_MEASURE)
        {
            for (int k = 1; k <= Lines; k++)
            {
                int line = k;
                float X = GetStartX(line);
                int cnt = 0;
                float diff, w1;
                w1 = -1;
                foreach (var note in ScoreBase.StaveNotes)
                {
                    if (note.line == line && note.visible)
                    {
                        if (w1 == -1) w1 = note.Width();
                        else
                        {
                            //Debug.LogError("cnt:" + cnt);
                            float w = note.Width();
                            X = X + w;
                            cnt++;
                        }

                        //if (note is FM_Chord)
                        //{
                        //    var chord = (FM_Chord)note;
                        //    if (chord != null)
                        //    {
                        //        foreach (var note2 in chord.Notes)
                        //        {
                        //            if(note2.stave == 1)
                        //                Debug.LogError("note:" + note2.note + "  oct:" + note2.octave);
                        //        }
                        //    }
                        //}
                    }
                }
                diff = (endX - X) / (cnt + 1);
                X = GetStartX(line) + w1;
                w1 = -1;
                if ((Align != FM_Align.ALIGN_LEFT_MEASURES && (Align == FM_Align.ALIGN_LEFT_LAST_MEASURE && line != Lines)) || diff < 0)
                {
                    foreach (var note in ScoreBase.StaveNotes)
                    {
                        if (note.line == line && note.visible)
                        {
                            if (w1 == -1)
                            {
                                w1 = 0;
                                note.SetDrawParameters(GetStartX(line), note.StartY1, note.StartY2);
                            }
                            else
                            {
                                float w = note.Width();
                                if (!note.visible) w = 0;
                                note.SetDrawParameters(X + diff, note.StartY1, note.StartY2);
                                X = X + w + diff;
                            }
                        }
                    }
                }
            }
        }

        ys1 = PaddingTop + GetPaddingVertical();
        ys2 = PaddingTop + GetPaddingVertical();
        int allLines = Lines;
        if (showRecordTrack)
            allLines = Lines * 2;

        for (int i = 1; i <= allLines; i++)
        {
            int line = i;
            if (showRecordTrack)
                line = i / 2 + 1;

            if (ScoreBase.StaveCount == FM_StaveCount._2)
                ys2 = ys1 + (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines());

            if (!showRecordTrack || i % 2 == 1)
            {
                foreach (var note in ScoreBase.StaveNotes)
                {
                    if (note.line == line)
                    {
                        if (note.stave == 0)
                            note.SetDrawParameters(note.StartX, ys1, ys2);
                        if (note.stave == 1)
                            note.SetDrawParameters(note.StartX, ys2, ys2);
                    }
                }
            }
            ys1 = ys2 + (GetDistanceBetweenRows() + 4 * GetDistanceBetweenStaveLines());
            ys2 = ys1;
        }
    }


    private int addedNote = -1;
    public int AddedNoteCount { get; set; }

    public void AddDynamicNote(FM_BaseNote note, float frequency)
    {
        if (addedNote == progressBar)
        {
            // FM_BaseNote lastNote = dynamicScore.StaveNotes[dynamicScore.StaveNotes.Count - 1];
            // dynamicScore.StaveNotes[dynamicScore.StaveNotes.Count - 1] = note;
            return;
        }
        addedNote = progressBar;
        if (progressBar > -1)
        {
            FM_BaseNote curNote = ScoreBase.StaveNotes[progressBar];

            int line = curNote.line;
            float x = curNote.StartX - 0.5f * curNote.Width();

            note.StartX = x;
            note.line = line;
            // canvas.drawRect(x, ys, x + FM_Const.dpTOpx(context, 1), ye, f);
        }

        dynamicScore.AddStaveNote(note);
        dynamicScore.StaveNotes.Add(note);
        UpdateDynamicNotes();

        AddedNoteCount++;

        int key = ScoreViewController.GetKeyWithNote(note.note, note.octave, note.accidental);
        int trackKey = FM_SoundPool.GetIndex(FM_Helper.NoteToString((FM_Note)note, GetKeySignature()));
        Debug.Log(frequency + " Add note: " + note.note + " Octave" + note.octave + " AddedNoteCount:" + AddedNoteCount + "  Count Key:" + key + "  Track Key:" + trackKey);
        // LogBaseNote(AddedNoteCount + " Add note:", note);
    }

    public void UpdateDynamicNotes()
    {
        List<FM_BaseNote> dynamicNotes = dynamicScore.StaveNotes;
        float endX = width - PaddingE - 2 * FM_Const.dpTOpx(FM_Const.DEFAULT_EXTRA_PADDING) - FM_Const.dpTOpx(10);
        float ys1 = PaddingTop + GetPaddingVertical();
        float ys2 = PaddingTop + GetPaddingVertical();

        int allLines = Lines;
        if (ShowRecordTrack)
            allLines = Lines * 2;

        for (int i = 0; i < allLines; i++)
        {
            int line = i + 1;
            if (ShowRecordTrack)
                line = i / 2 + 1;

            if (ScoreBase.StaveCount == FM_StaveCount._2)
                ys2 = ys1 + (GetDistanceBetweenStaves() + 4 * GetDistanceBetweenStaveLines());

            if (!ShowRecordTrack || i % 2 == 1)
            {
                for (int j = 0; j < dynamicNotes.Count; j++)
                {
                    if (dynamicNotes[j].line == line)
                    {
                        if (dynamicNotes[j].stave == 0)
                            dynamicNotes[j].SetDrawParameters(dynamicNotes[j].StartX, ys1, ys2);
                        if (dynamicNotes[j].stave == 1)
                            dynamicNotes[j].SetDrawParameters(dynamicNotes[j].StartX, ys2, ys2);
                    }
                }
            }
            ys1 = ys2 + (GetDistanceBetweenRows() + 4 * GetDistanceBetweenStaveLines());
            ys2 = ys1;
        }
    }

    public void ClearDynamicNotes()
    {
        // rightNWrong.Clear();
        notesRight.Clear();
        if (dynamicScore != null)
            dynamicScore.ClearStaveNotes();
        addedNote = -1;
    }

    public int GetIndex(FM_Note n)
    {
        return FM_SoundPool.GetIndex(FM_Helper.NoteToString(n, GetKeySignature()));
    }

    public FM_Align GetNotesAlign()
    {
        return Align;
    }

    public void SetNotesAlign(FM_Align align)
    {
        Align = align;
        ComputeLines();
    }

    public void AddToTie(string tie, FM_Note n)
    {
        ScoreBase.AddToTie(tie, n);
    }

    public void BeginTuplet(string s)
    {
        ScoreBase.BeginTuplet(s);
    }

    public void AddToTuplet(FM_BaseNote n)
    {
        ScoreBase.AddToTuplet(n);
    }

    public void EndTuplet()
    {
        ScoreBase.EndTuplet();
    }

    public void BeginBeam()
    {
        ScoreBase.BeginBeam();
    }

    public void AddToBeam(FM_Note n)
    {
        ScoreBase.AddToBeam(n);
    }

    public void EndBeam()
    {
        ScoreBase.EndBeam();
    }

    public void SetNoteSpacing(float noteSpacing)
    {
        NoteSpacing = FM_Const.dpTOpx(noteSpacing);
    }

    public bool IsMultiRow()
    {
        return MultiRow;
    }

    public void SetMultiRow(bool multiRow)
    {
        MultiRow = multiRow;
        ComputeLines();
    }

    public bool IsAllowZoomPan()
    {
        return AllowZoomPan;
    }

    public bool IsAllowZoomControls()
    {
        return AllowZoomControls;
    }

    public void SetAllowZoomControls(bool allowZoomControls)
    {
        //AllowZoomControls = allowZoomControls;
        //tmpZoomControls = allowZoomControls;
        //if (allowZoomControls)
        //{
        //    ISharedPreferences settings = PreferenceManager.GetDefaultSharedPreferences(context);
        //    float d = settings.GetFloat("zoom_level", FM_Const.pxTOdp(context, _DistanceBetweenStaveLines));
        //    SetDistanceBetweenStaveLines(d);
        //}
        //Invalidate();
    }

    public void SetAllowZoomPan(bool allowZoomPan)
    {
        AllowZoomPan = allowZoomPan;
    }

    public Color GetStaveLineColor()
    {
        return StaveLineColor;
    }

    public void SetStaveLineColor(Color staveLineColor)
    {
        StaveLineColor = staveLineColor;
    }

    //private class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener
    //{
    //    public override bool OnScale(ScaleGestureDetector detector)
    //    {
    //        float prevScale = mScaleFactor;
    //        mScaleFactor *= detector.ScaleFactor;
    //        // Don't let the object get too small or too large.
    //        mScaleFactor = Math.Max(1.0f, Math.Min(mScaleFactor, 5.0f));

    //        float adjustedScaleFactor = mScaleFactor / prevScale;
    //        pivotPointX = detector.FocusX;
    //        pivotPointY = detector.FocusY;
    //        float adjustY = 0;
    //        if (CenterVertical) adjustY = GetMeasuredHeight_FM() / 2f - GetDrawHeight() / 2f;
    //        pivotPointY -= adjustY;
    //        mPosX += (mPosX - pivotPointX) * (adjustedScaleFactor - 1);
    //        mPosY += (mPosY - pivotPointY) * (adjustedScaleFactor - 1);
    //        Invalidate();
    //        return true;
    //    }
    //}

    //private float GetMeasuredHeight_FM()
    //{
    //    return Math.Max(LayoutParameters.Height, MeasuredHeight);
    //}

    public int GetNoteCount()
    {
        return ScoreBase.GetNoteCount();
    }

    public FM_BaseNote GetNote(int index)
    {
        return ScoreBase.GetNote(index);
    }

    public FM_BaseNote GetLastNote()
    {
        return ScoreBase.GetLastNote();
    }

    public int LoadFromJson(JObject obj)
    {
        int ret = ScoreBase.LoadFromJson(obj);

        dynamicScore.SetTimeSignature(ScoreBase.GetTimeSignature_n(), ScoreBase.GetTimeSignature_d());
        dynamicScore.SetKeySignature(ScoreBase.GetKeySignature());
        dynamicScore.SetVoiceCount(ScoreBase.GetVoiceCount());
        dynamicScore.SetFirstStaveClef(ScoreBase.GetFirstStaveClef());
        dynamicScore.SetSecondStaveClef(ScoreBase.GetSecondStaveClef());
        SetNotesAlign(FM_Align.ALIGN_LEFT_LAST_MEASURE);

        // LogAllNotes();
        return ret;
    }

    public void LogAllNotes()
    {
        for (int i = 0; i < ScoreBase.StaveNotes.Count; i++)
        {
            FM_BaseNote note = ScoreBase.StaveNotes[i];
            if (note is FM_Chord)
            {
                FM_Chord chord = (FM_Chord)note;
                foreach (FM_BaseNote baseNote in chord.Notes)
                {
                    if (baseNote is FM_Note)
                        LogNote("LogAllNotes note index " + i + ":", (FM_Note)baseNote);
                }
            }
        }
    }

    public void LogBaseNote(string msg, FM_BaseNote note)
    {
        Debug.Log(msg + $"note:{note.note} octave:{note.octave} accidental:{note.accidental} duration:{note.duration} voice:{note.voice} line:{note.line}");
    }

    public void LogNote(string msg, FM_Note note)
    {
        FM_Note fmNote = note;
        if (fmNote != null)
            Debug.Log(msg + $"note:{fmNote.note} octave:{fmNote.octave} accidental:{fmNote.accidental} duration:{fmNote.duration} voice:{fmNote.voice} line:{fmNote.line}");
    }

    public void ShowScore(int measures, bool blur)
    {
        // finishedDraw = new CountDownLatch(1);
        if (measures == 0) AllowZoomControls = tmpZoomControls;
        else AllowZoomControls = false;
        //new Thread(() =>
        {
            for (int i = 0; i < ScoreBase.StaveNotes.Count; i++)
            {
                if (!(ScoreBase.StaveNotes[i] is FM_Clef)) ScoreBase.StaveNotes[i].visible = true;
                ScoreBase.StaveNotes[i].SetBlurred(false);
                if (ScoreBase.StaveNotes[i] is FM_BarNote && ((FM_BarNote)ScoreBase.StaveNotes[i]).lineEnd)
                {
                    ScoreBase.StaveNotes[i].visible = false;
                }
            }
            //            this.post(this::invalidate);
            //            try {
            //                finishedDraw.await();
            //                finishedDraw = null;
            //            } catch (Exception ignored) {
            //            }
            if (measures != 0)
            {
                int i = 0;
                int bars = 0;
                while (i < ScoreBase.StaveNotes.Count)
                {
                    if (ScoreBase.StaveNotes[i] is FM_BarNote)
                    {
                        bars++;
                        i++;
                        continue;
                    }
                    if (bars >= measures)
                    {
                        if (blur) ScoreBase.StaveNotes[i].SetBlurred(true);
                        ScoreBase.StaveNotes[i].visible = false;
                    }
                    i++;
                }
            }
            //Post(Invalidate);
        }
        //).Start();
    }

}